home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcl / src-16f.lha / ldb / egets.c < prev    next >
C/C++ Source or Header  |  1991-11-06  |  17KB  |  629 lines

  1. /* $Header: egets.c,v 1.1 90/02/24 19:37:15 wlott Exp $ */
  2. /********************************************************************
  3. *                                                                   *
  4. *                                                                   *
  5. *     Copyright (C) 1987, Carnegie Mellon University                *
  6. *                                                                   *
  7. *                                                                   *
  8. ********************************************************************/
  9.  
  10. /* egets.c --    replacement for C library's gets(3s)
  11.  *        allows emacs style command editing if stdin is a tty
  12.  *                        Derek Beatty
  13.  * HISTORY
  14.  *    20 Nov 87 Beatty: added code for more-mode.
  15.  *        More-mode is handled by this file so gets can reset
  16.  *        its line count.
  17.  */
  18. #include <stdio.h>
  19. #include <ctype.h>
  20. #include <sgtty.h>
  21. #include <signal.h>
  22. #include <strings.h>
  23. #include <setjmp.h>
  24.  
  25. int getpid();        /* this should have been declared somewhere */
  26.  
  27. /*LINTLIBRARY*/
  28.  
  29. #define TRUE (1)
  30. #define FALSE (0)
  31.  
  32. typedef unsigned char uchar;
  33.  
  34. #define HISTBUF        (2000)
  35. #define LINEBUF        (132)
  36.  
  37. #define CONTROL(x)    ((x)&0x1F)
  38. #define META(x)        ((x)|0x80)
  39. #define BELL        CONTROL('g')
  40. #define ESC        (0x1B)
  41. #define RUBOUT        (0x7F)
  42. #define ISCONTROL(x)    ((x)<0x20 || (x)>0x7E)        /* includes meta */
  43. #define NOTPREFIX(c)    ((c)!=ESC)
  44.  
  45. /** Global Variables for user settings */
  46. int ScrollPauseSwitch;
  47. int ScreenLengthLimit;
  48. int EditSwitch = TRUE;
  49.  
  50. /** Shared by MORE and GETS **/
  51. static int CurrentLine = 1;
  52. static int Initialized = FALSE;/* true iff initialize has been called */
  53.  
  54. /** VARIABLES FOR GETS **/
  55. static struct sgttyb OttyFlags, NttyFlags;
  56. static struct ltchars OltChars, NltChars;
  57.   
  58. #ifdef SIGTSTP
  59. static int (*TstpHandler)();
  60. #endif SIGTSTP
  61.   
  62. static int Active = TRUE;    /* true iff egets is working */
  63. static uchar History[HISTBUF];    /* history buffer */
  64. static uchar
  65.     *FirstHistory,        /* ptr to earliest line in history buffer */
  66.     *YankedHistory;        /* ptr to history last yanked */
  67. static uchar KillBuf[LINEBUF+1];    /* kill buffer */
  68. static uchar Line[LINEBUF+1];    /* line being entered, plus null */
  69. static uchar
  70.     *Cursor,        /* ptr to character cursor is on */
  71.     *Bol,            /* ptr to first char of line */
  72.     *Eol;            /* ptr to null that terminates line */
  73.  
  74. static uchar tLine[LINEBUF+1];    /* line displayed on terminal */
  75. static uchar *tCursor, *tBol, *tEol;
  76.  
  77. static uchar Meta = 0;    /* meta bit; 0x80 if meta key was last */
  78. static int Bleep = FALSE;   /* TRUE iff bell needed at next screen update */
  79.  
  80. static jmp_buf out_of_here;
  81.  
  82. static void saveTtyModes(), restoreTtyModes(), initialize(), showLine(),
  83.     killToEol(), yankNext(), yankPrev(),    yankKillBuf(), 
  84.     transpose(), openSpace(), closeSpace();
  85.  
  86. /* saveTtyModes -- save tty state and put tty into cbreak mode */
  87. static void 
  88. saveTtyModes()
  89. {
  90.     (void) ioctl(fileno(stdin), TIOCGETP, (char*) &OttyFlags);
  91.     NttyFlags = OttyFlags;
  92.     NttyFlags.sg_flags |= CBREAK;
  93.     NttyFlags.sg_flags &= ~XTABS;
  94.     NttyFlags.sg_flags &= ~ECHO;
  95.     (void) ioctl(fileno(stdin), TIOCSETN, (char*) &NttyFlags);
  96.     (void) ioctl(fileno(stdin), TIOCGLTC, (char*) &OltChars);
  97.     NltChars = OltChars;
  98.     NltChars.t_dsuspc = NltChars.t_suspc;
  99.     (void) ioctl(fileno(stdin), TIOCSLTC, (char*) &NltChars);
  100. }
  101.  
  102. /* restoreTtyModes -- restore tty state */
  103. static void 
  104. restoreTtyModes()
  105. {
  106.     (void) ioctl(fileno(stdin), TIOCSLTC, (char*) &OltChars);
  107.     (void) ioctl(fileno(stdin), TIOCSETN, (char*) &OttyFlags);
  108. }
  109.  
  110. /* isaCRT -- return nonzero iff fd is a tty with CRT bits set */
  111. static int 
  112. isaCRT( fd)
  113. int fd;
  114. {
  115.     int             ltbits;
  116.  
  117.     (void) ioctl(fd, TIOCLGET, (char*) <bits);
  118.     return (ltbits & (LCRTERA | LCRTKIL));
  119. }
  120.  
  121. /* tstp -- handler for SIGTSTP: restore tty and suspend */
  122. #ifdef SIGTSTP
  123. int 
  124. tstp()
  125. {
  126.     if (TstpHandler == SIG_IGN)
  127.     return;
  128.     restoreTtyModes();
  129.     if (TstpHandler == SIG_DFL)
  130.     (void) kill(getpid(), SIGSTOP);
  131.     else
  132.     (*TstpHandler) ();
  133.     saveTtyModes();
  134.     tBol = tEol = tCursor = tLine;
  135.     tLine[0] = '\0';
  136.     showLine();
  137. }
  138. #endif
  139.  
  140. /* initialize -- init data structures used by gets and more. called once. */
  141. static void 
  142. initialize()
  143. {
  144.     int             i;
  145.  
  146.     /* initialization for gets */
  147.     Initialized = TRUE;
  148.     YankedHistory = FirstHistory = History;
  149.     for (i = 0; i < HISTBUF; i++)
  150.     History[i] = '\0';
  151.     KillBuf[0] = '\0';
  152.  
  153.     /* initialization for more */
  154.     ScrollPauseSwitch= isaCRT(fileno(stdin)) && isatty(fileno(stdout));
  155.  
  156.     ScreenLengthLimit= -1;
  157. #ifdef TIOCGWINSZ /* 4.3bsd window size */
  158.     {
  159.     struct winsize wss;
  160.     ioctl(fileno(stdin), TIOCGWINSZ, (char*) &wss);
  161.     ScreenLengthLimit= wss.ws_row-1;
  162.     }
  163. #endif
  164. #ifdef TIOCGSIZE /* sun window size */
  165.     {
  166.     struct ttysize tss;
  167.     ioctl(fileno(stdin), TIOCGSIZE, (char*) &tss);
  168.     ScreenLengthLimit= tss.ts_lines-1;
  169.     }
  170. #endif
  171.     if (ScreenLengthLimit==-1)
  172.     ScreenLengthLimit = 24;
  173. }
  174.  
  175.  
  176. /* killToEol -- delete characters on and following cursor into kill buffer */
  177. static void 
  178. killToEol()
  179. {
  180.     int             n = strlen( (char*) Cursor);
  181.  
  182.     (void) strcpy((char*) KillBuf, (char*) Cursor);
  183.     closeSpace(n);
  184. }
  185.  
  186. /* transpose -- transpose two characters preceding cursor */
  187. static void 
  188. transpose()
  189. {
  190.     if (Cursor <= Bol + 1)
  191.     Bleep = TRUE;
  192.     else {
  193.     uchar           c = *(Cursor - 1);
  194.     *(Cursor - 1) = *(Cursor - 2);
  195.     *(Cursor - 2) = c;
  196.     }
  197. }
  198.  
  199. /* numInWord -- return number of chars through word before or after cursor */
  200. static int
  201. numInWord( before)
  202.   int before;        /* true iff want count in word before cursor */
  203. {
  204.     uchar          *tempCursor = Cursor;
  205.     int             n;
  206.  
  207.     if (before) {
  208.     if (tempCursor > Bol)
  209.         tempCursor--;
  210.     else
  211.         Bleep = TRUE;
  212.     while (tempCursor > Bol && !isalnum(*tempCursor))
  213.         tempCursor--;
  214.     while (tempCursor > Bol && isalnum(*tempCursor))
  215.         tempCursor--;
  216.     n = Cursor - tempCursor;
  217.     if (n != 0 && !isalnum(*tempCursor))
  218.         n--;
  219.     return (n);
  220.     } else {
  221.     if (tempCursor >= Eol)
  222.         Bleep = TRUE;
  223.     while (tempCursor < Eol && !isalnum(*tempCursor))
  224.         tempCursor++;
  225.     while (tempCursor < Eol && isalnum(*tempCursor))
  226.         tempCursor++;
  227.     return (tempCursor - Cursor);
  228.     }
  229. }
  230.  
  231. /* yankHistory -- yank line at YankedHistory into Line */
  232. static void 
  233. yankHistory()
  234. {
  235.     uchar          *tempHistory;
  236.  
  237.     YankedHistory++;
  238.     if (YankedHistory >= History + HISTBUF)
  239.     YankedHistory = History;
  240.     tempHistory = YankedHistory;
  241.     Bol = Cursor = Line;
  242.     while (*tempHistory) {
  243.     *Cursor++ = *tempHistory++;
  244.     if (tempHistory >= History + HISTBUF)
  245.         tempHistory = History;
  246.     }
  247.     *Cursor = '\0';
  248.     Eol = Cursor;
  249. }
  250.  
  251. /* yankNext -- yank the next line from the history buffer */
  252. static void 
  253. yankNext()
  254. {
  255.     while (*YankedHistory) {
  256.     YankedHistory++;
  257.     if (YankedHistory >= History + HISTBUF)
  258.         YankedHistory = History;
  259.     }
  260.     yankHistory();
  261. }
  262.  
  263. /* yankPrev -- yank the previous line from the history buffer */
  264. static void 
  265. yankPrev()
  266. {
  267.     YankedHistory--;
  268.     if (YankedHistory < History)
  269.     YankedHistory = History + HISTBUF - 1;
  270.     YankedHistory--;
  271.     if (YankedHistory < History)
  272.     YankedHistory = History + HISTBUF - 1;
  273.     while (*YankedHistory) {
  274.     YankedHistory--;
  275.     if (YankedHistory < History)
  276.         YankedHistory = History + HISTBUF - 1;
  277.     }
  278.     yankHistory();
  279. }
  280.  
  281. /* addHistory -- add the current line to the history buffer */
  282. static void 
  283. addHistory()
  284. {
  285.     uchar          *tempCursor = Bol;
  286.  
  287.     while (*tempCursor) {
  288.     *FirstHistory++ = *tempCursor++;
  289.     if (FirstHistory >= History + HISTBUF)
  290.         FirstHistory = History;
  291.     }
  292.     *FirstHistory++ = '\0';
  293.     if (FirstHistory >= History + HISTBUF)
  294.     FirstHistory = History;
  295.     YankedHistory = FirstHistory;
  296. }
  297.  
  298. /* yankKillBuf -- insert kill buffer in current line.  Bleep if trouble */
  299. static void 
  300. yankKillBuf()
  301. {
  302.     uchar          *sourceCursor = KillBuf;
  303.     uchar          *destCursor = Cursor;
  304.     int             n = strlen((char*) KillBuf);
  305.  
  306.     if (Eol + n > Line + LINEBUF)
  307.     Bleep = TRUE;
  308.     else {
  309.     openSpace(n);
  310.     while (*sourceCursor)
  311.         *destCursor++ = *sourceCursor++;
  312.     Cursor += n;
  313.     }
  314. }
  315.  
  316. /* redraw -- force showLine to redraw current line on screen */
  317. static void 
  318. redraw()
  319. {
  320.     tCursor = tBol = tEol = tLine;
  321.     tLine[0] = '\0';
  322.     printf("\n");
  323.     (void) fflush(stdout);
  324. }
  325.  
  326. /* showLine -- update screen and tLine to reflect Line */
  327. static void 
  328. showLine()
  329. {
  330.     uchar          *Dif, *tDif;
  331.  
  332.  
  333.     /* find first place the lines differ */
  334.     Dif = Bol;
  335.     tDif = tBol;
  336.     while (*Dif && *tDif && *Dif == *tDif) {
  337.     Dif++;
  338.     tDif++;
  339.     }
  340.     if (*Dif || *tDif) {
  341.     /* lines differ: move tCursor to the point of difference */
  342.     while (tCursor > tDif) {
  343.         printf("\b");
  344.         tCursor--;
  345.     }
  346.     while (tCursor < tDif)
  347.         printf("%c", *tCursor++);
  348.     /* write remainder of Line */
  349.     while (*Dif)
  350.         printf("%c", *tCursor++ = *Dif++);
  351.     /* add blanks to bring tLine up to length of Line */
  352.     while (tCursor < tEol) {
  353.         printf(" ");
  354.         *tCursor++ = '\0';
  355.     }
  356.     /* adjust tEol */
  357.     tEol = tBol + (Eol - Bol);
  358.     }
  359.     /* move tCursor to proper place */
  360.     while (tCursor - tBol > Cursor - Bol) {
  361.     printf("\b");
  362.     tCursor--;
  363.     }
  364.     while (tCursor - tBol < Cursor - Bol)
  365.     printf("%c", *tCursor++);
  366.     /* cleanup */
  367.     if (Bleep) {
  368.     printf("%c", BELL);
  369.     Bleep = FALSE;
  370.     }
  371.     (void) fflush(stdout);
  372. }
  373.  
  374. /* openSpace -- open space for n characters at cursor.  Beep if trouble. */
  375. static void 
  376. openSpace( n)
  377.   int n;
  378. {
  379.     uchar          *sourceCursor, *destCursor;
  380.  
  381.     if (n == 0)
  382.     return;
  383.     destCursor = Eol + n;
  384.     if (destCursor > Line + LINEBUF + 1)
  385.     Bleep = TRUE;
  386.     else {
  387.     sourceCursor = Eol;
  388.     Eol = destCursor;
  389.     while (sourceCursor >= Cursor)
  390.         *destCursor-- = *sourceCursor--;
  391.     }
  392. }
  393.  
  394. /* closeSpace -- close space for n characters at cursor.  Bleep if trouble. */
  395. static void 
  396. closeSpace( n)
  397.   int n;
  398. {
  399.     uchar          *sourceCursor, *destCursor;
  400.  
  401.     if (n == 0)
  402.     return;
  403.     if (n > Eol - Cursor) {
  404.     Bleep = TRUE;
  405.     n = Eol - Cursor;
  406.     }
  407.     sourceCursor = Cursor + n;
  408.     destCursor = Cursor;
  409.     while (sourceCursor <= Eol)
  410.     *destCursor++ = *sourceCursor++;
  411.     Eol = destCursor - 1;
  412. }
  413.  
  414. /* ogets -- get a line without command line editing */
  415. static char *
  416. ogets(s, n)
  417. char *s;
  418. int n;                /* total length of string */
  419. {
  420.     int c, i;
  421.     char *t;
  422.  
  423.     t = s; i = n;
  424.  
  425.     if (n <= 0)
  426.         return NULL;
  427.  
  428.     while ((--i > 0) && ((c = getchar()) != '\n') && (c != EOF))
  429.         *t++ = c;
  430.     if ((n != 1) && (c == EOF) && (s == t))
  431.         return NULL;
  432.     *t = '\0';
  433.  
  434.     return(s);
  435. }
  436.  
  437. static sigint_handler()
  438. {
  439.     longjmp(out_of_here, 1);
  440. }
  441.  
  442.  
  443. /* egets -- get a line from stdin with command line editing */
  444. char * 
  445. egets()
  446. {
  447.     uchar           c= '\0';
  448.     int            done, returnEOF;
  449.     long int        buffered= 0L;
  450.     int             n;
  451.     int             arg = 0;
  452.     int            gettingArg = FALSE, gettingDigitArg = FALSE;
  453.     int            stillGettingArg = FALSE;
  454.     struct sigvec sv, old_int_sv;
  455.  
  456.     if (!EditSwitch
  457.     || !Active
  458.     || (!Initialized && (!isatty(fileno(stdin)) ))
  459.     || !isaCRT(fileno(stdin))) {
  460.     Active = FALSE;
  461.     return (ogets(Line, LINEBUF + 1));
  462.     }
  463.     if (!Initialized)
  464.     initialize();
  465.     fflush(stdout);
  466.     CurrentLine=1;        /* Reset line count for more-mode. */
  467.     Line[0] = '\0';
  468.     tLine[0] = '\0';
  469.     Cursor = Bol = Eol = Line;
  470.     tCursor = tBol = tEol = tLine;
  471. #ifdef SIGTSTP
  472.     TstpHandler = signal(SIGTSTP, tstp);
  473. #endif
  474.     saveTtyModes();
  475.     if (setjmp(out_of_here))
  476.         returnEOF = TRUE;
  477.     else {
  478.         sv.sv_handler = sigint_handler;
  479.         sv.sv_mask = 0;
  480.         sv.sv_flags = 0;
  481.         sigvec(SIGINT, &sv, &old_int_sv);
  482.  
  483.         returnEOF = done = FALSE;
  484.         while (!done) {
  485.             /* lint complains that c may be used before set */
  486.             if (!gettingArg && arg > 0 && NOTPREFIX(c))
  487.                 arg--;
  488.             else {
  489.                 if (read(fileno(stdin), (char*) &c, 1) == 0)
  490.                     c = CONTROL('m');
  491.                 else {
  492.                     c |= Meta;
  493.                     Meta = 0;
  494.                 }
  495.             }
  496.             if (ISCONTROL(c)) {
  497.                 switch (c) {
  498.                     case CONTROL('a'):
  499.                         Cursor = Bol;
  500.                         break;
  501.                     case CONTROL('b'):
  502.                         if (Cursor > Bol)
  503.                             Cursor--;
  504.                         else
  505.                             Bleep = TRUE;
  506.                         break;
  507.                     case CONTROL('d'):
  508.                         if (Cursor < Eol)
  509.                             closeSpace(1);
  510.                         else
  511.                             Bleep = TRUE;
  512.                         break;
  513.                     case CONTROL('e'):
  514.                         Cursor = Eol;
  515.                         break;
  516.                     case CONTROL('f'):
  517.                         if (Cursor < Eol)
  518.                             Cursor++;
  519.                         else
  520.                             Bleep = TRUE;
  521.                         break;
  522.                     case CONTROL('h'):
  523.                     case RUBOUT:
  524.                         if (Cursor > Bol) {
  525.                             Cursor--;
  526.                             closeSpace(1);
  527.                         } else
  528.                             Bleep = TRUE;
  529.                         break;
  530.                     case CONTROL('j'):
  531.                         done = TRUE;
  532.                         break;
  533.                     case CONTROL('k'):
  534.                         killToEol();
  535.                         break;
  536.                     case CONTROL('m'):
  537.                         done = TRUE;
  538.                         break;
  539.                     case CONTROL('n'):
  540.                         yankNext();
  541.                         break;
  542.                     case CONTROL('p'):
  543.                         yankPrev();
  544.                         break;
  545.                     case CONTROL('r'):
  546.                         redraw();
  547.                         break;
  548.                     case CONTROL('t'):
  549.                         transpose();
  550.                         break;
  551.                     case CONTROL('u'):
  552.                         if (gettingArg)
  553.                             arg *= 4;
  554.                         else
  555.                             arg = 4;
  556.                         stillGettingArg = TRUE;
  557.                         gettingDigitArg = FALSE;
  558.                         break;
  559.                     case CONTROL('y'):
  560.                         yankKillBuf();
  561.                         break;
  562.                     case ESC:
  563.                         Meta = 0x80;
  564.                         break;
  565.                     case META('f'):
  566.                         Cursor += numInWord(0);
  567.                         break;
  568.                     case META('b'):
  569.                         Cursor -= numInWord(1);
  570.                         break;
  571.                     case META('d'):
  572.                         closeSpace(numInWord(0));
  573.                         break;
  574.                     case CONTROL('w'):
  575.                     case META('h'):
  576.                         n = numInWord(1);
  577.                         Cursor -= n;
  578.                         closeSpace(n);
  579.                         break;
  580.                     case META(CONTROL('d')):
  581.                         if (Bol == Eol)
  582.                             done = returnEOF = TRUE;
  583.                         else
  584.                             Bleep = TRUE;
  585.                         break;
  586.                     default:
  587.                         Bleep = TRUE;
  588.                         break;
  589.                 }
  590.             } else if (!gettingArg || !isdigit(c)) {
  591.                 if (Eol + 1 > Line + LINEBUF)
  592.                     Bleep = TRUE;
  593.                 else {
  594.                     openSpace(1);
  595.                     *Cursor++ = c;
  596.                 }
  597.             } else {
  598.                 /* getting argument digits */
  599.                 if (gettingDigitArg)
  600.                     arg = 10 * arg + c - '0';
  601.                 else
  602.                     arg = c - '0';
  603.                 stillGettingArg = TRUE;
  604.                 gettingDigitArg = TRUE;
  605.             }
  606.             if (gettingArg && !stillGettingArg)
  607.                 arg--;
  608.             gettingArg = stillGettingArg;
  609.             stillGettingArg = FALSE;
  610.             if (Bleep)
  611.                 arg = 0;
  612.             (void) ioctl(fileno(stdin), FIONREAD, (char*) &buffered);
  613.             if ((buffered == 0L && arg == 0) || done)
  614.                 showLine();
  615.         }
  616.         addHistory();
  617.     }
  618.  
  619.     if (!returnEOF) printf("\n");
  620.     (void) fflush(stdout);
  621.  
  622.     restoreTtyModes();
  623. #ifdef SIGTSTP
  624.     (void) signal(SIGTSTP, TstpHandler);
  625. #endif
  626.     sigvec(SIGINT, &old_int_sv, NULL);
  627.     return (returnEOF ? NULL : (char *) Line);
  628. }
  629.